from django.db import models
from django import forms
#from settings import MyConstants
#from parameter.models import JrcParameter
import datetime
from django.db.models.aggregates import Max
from django.db import connection
import time
from jrcconfig.models import JrcConfig
from tz.views import JrcTimeZone
from decimal import Decimal
from django.core.exceptions import MultipleObjectsReturned

class MyConstants:

    ASSET_CLASSES_CHOICES = (
        (r"Fx", "Forex")
        , (r"Ft", "Futures")
    )

    DECIMAL_PLACES = (
        (1, u"1")
        , (2, u"10")
        , (3, u"100")
        , (4, u"1.000")
        , (5, u"10.000")
    )

    # Available currencies
    CURRENCY_CHOICES = (
        (u"AUD", u"AUD")
        , (u"CAD", u"CAD")
        , (u"CHF", u"CHF")
        , (u"GBP", u"GBP")
        , (u"EUR", u"EUR")
        , (u"JPY", u"JPY")
        , (u"NZD", u"NZD")
        , (u"USD", u"USD")
    )

    CASH_OR_FUTURE = (
        (u"C", u"Cash")
        , (u"F", u"Future")
    )

    # Long or Short
    DIRECTION_CHOICES = (
        (u"L", u"Long (buy)")
        , (u"S", u"Short (sell)")
        , (u"B", u"Both")
    )
    
    STATUS_APPROVED = 1
    STATUS_DELETED = 2
    
    CST_STATUS_TESTED = 1
    CST_STATUS_IMPLEMENTED = 2
    CST_STATUS_SORTED_OUT = 3
    CST_STATUS_DELETED = 4
    
    
    STATUS_CHOICES = (
        (1, u"approved")
        , (2, u"deleted")
    )
    
    STATUS_NAMES = {
        1: "approved"
        , 2: "deleted"
    }

    # Status for CST entries
    CST_STATUS_CHOICES = (
        (CST_STATUS_TESTED, r"tested")
        , (CST_STATUS_IMPLEMENTED, r"implemented")
        , (CST_STATUS_SORTED_OUT, r"sorted out")
        , (CST_STATUS_DELETED, r"deleted")                     
    )
    
    SESSION_CHOICES = (
        (u"N", u"natural")
        , (u"S", u"session")
    )

    DATE_FMT_CHOICES = (
        (r"%m/%d/%Y", r"1/28/2012")
        , (r"%d.%m.%Y", r"28.1.2012")
        , (r"%Y-%m-%d", r"2012-01-28")
    )
    
    @staticmethod
    def get_session_name(value_id):
        """
        Gets name of session - natural or session
        """
        v = value_id
        if value_id == "N":
            v = "natural"
        elif value_id == "S":
            v = "session"
        return v
    
    @staticmethod
    def get_direction_name(value_id):
        """
        Gets name of direction by abbrevation
        """
        s = value_id
        if value_id == u"L":
            s = u"Long (buy)"
        elif value_id == u"S":
            s = u"Short (sell)"
        elif value_id == u"B":
            s = u"Both"

        return s

    @staticmethod
    def get_status_image(status_id):
        return "%s" % MyConstants.get_status_name(status_id)
    
    @staticmethod
    def get_status_name(status_id):
        return "%s" % MyConstants.STATUS_NAMES[status_id]
    
    @staticmethod
    def get_currency_symbol(currency_code):
        """
        Returns HTML symbol for currency or its name
        """
        v = currency_code.upper()
        if v == "EUR":
            return "&euro;"
        if v == "USD":
            return "$"
        if v == "GBP":
            return "&pound;" 
        
        return v

    @staticmethod
    def try_convert_date_to_tm(sdate):
        """ Tries to convert string date to struct_time w/o excpetion! """
        print "Parsing date: %s" % sdate
        tm = None
        try:
            tm = time.strptime(sdate, "%m/%d/%Y")
        except:
            try:
                tm = time.strptime(sdate, "%d.%m.%Y")
            except:
                tm = None
        return tm

    @staticmethod
    def convert_string_to_decimal(sprice):
        """ Parses and returns decimal from string. Possible separators are dot and comma """
        # Not very reliable solution. Assume that no other commas will be
        sprice = sprice.replace(',', '.')
        print "Going to convert to decimal '%s'" % sprice
        return Decimal(sprice)


class StockExchange(models.Model):
    status_id = models.IntegerField(default = MyConstants.STATUS_APPROVED, choices = MyConstants.STATUS_CHOICES)
    name = models.CharField(max_length = 128)
    label = models.CharField(max_length = 32, blank = True)
    
    def status_name(self):
        return MyConstants.get_status_name(self.status_id)
    
    def __unicode__(self):
        return self.name

class TimeLevel(models.Model):
    status_id = models.IntegerField(choices = MyConstants.STATUS_CHOICES)
    name = models.CharField(max_length = 64)
    minutes = models.IntegerField()
    priority = models.IntegerField(default = 0)
    
    def __unicode__(self):
        return "%s" % self.name

class Asset(models.Model):
    # Approved, deleted, etc..
    status_id = models.IntegerField(default = 1, choices = MyConstants.STATUS_CHOICES)
    
    name = models.CharField(max_length = 128, unique=True)
    priority = models.IntegerField(default = 0)
    
    created_at = models.DateTimeField(auto_now_add = True)
    
    def __unicode__(self):
        return "%s" % self.name
    
    def status_name(self):
        return "%s" % MyConstants.get_status_name(self.status_id)
    

class Market(models.Model):
    # Approved, deleted, etc..
    status_id = models.IntegerField(default = 1, choices = MyConstants.STATUS_CHOICES)
    
    name = models.CharField(max_length = 128, unique = True)
    stock_exchange_id = models.ForeignKey(StockExchange)
#    stock_exchange = models.CharField(max_length = 32, choices = MyConstants.STOCK_EXCHANGE_CHOICES, blank = True)
    label = models.CharField(max_length = 128, blank = True)
    currency_code = models.CharField(max_length = 4, choices = MyConstants.CURRENCY_CHOICES)

    # How many points are in contract
    points = models.DecimalField(max_digits = 14, decimal_places = 6)

    # Price of 1 point
    point_price = models.DecimalField(max_digits = 8, decimal_places = 5)

    # How many digits after comma
    price_decimal_places = models.IntegerField(default = 1, choices = MyConstants.DECIMAL_PLACES)

    start = models.TimeField()
    end = models.TimeField()
    time_zone = models.CharField(max_length = 32, choices = JrcTimeZone.UTC_ZONES, default = "UTC")

    asset = models.ForeignKey(Asset, verbose_name = "Asset class")
    priority = models.IntegerField(default = 0)
    
    created_at = models.DateTimeField(auto_now_add = True)
    
    def __unicode__(self):
        return "%s" %self.name
    
    def status_name(self):
        return "%s" % MyConstants.get_status_name(self.status_id)

    def get_points_fmt(self):
        decimal_places = self.price_decimal_places
        if decimal_places < 1 or decimal_places > 5:
            decimal_places = 1
        fmt = "%s.%df" % ("%", decimal_places)
#        print fmt
        return fmt % self.points

    def get_price_of_points(self):
        if (self.point_price < 1) or (self.points < 1):
            return 0.0
        return "%.1f" % (self.point_price * self.points)
    
    def get_currency_symbol(self):
        return MyConstants.get_currency_symbol(self.currency_code)
    
    

class JrcSystem(models.Model):
    name = models.CharField(max_length=128)
    status_id = models.IntegerField(default=1, choices = MyConstants.STATUS_CHOICES)
    source_code = models.TextField()
    priority = models.IntegerField(default=0)
    
    description = models.CharField(max_length = 512, blank = True)

    created_at = models.DateField(default = datetime.datetime.now())
    
    def __unicode__(self):
        return "%s" % self.name
    
    def status_name(self):
        return MyConstants.get_status_name(self.status_id)
    
class JrcParameter(models.Model):
    system_id = models.ForeignKey(JrcSystem)
    market_id = models.ForeignKey(Market)
    status_id = models.IntegerField(default = 1, choices = MyConstants.STATUS_CHOICES)
    direction_id = models.CharField(max_length = 2, choices = MyConstants.DIRECTION_CHOICES)
    cash_or_future = models.CharField(max_length = 2, choices = MyConstants.CASH_OR_FUTURE)
    day_begin = models.DateField(null = True, blank = True)
    day_end = models.DateField(null = True, blank = True)
    source_code = models.TextField(blank = True)
    priority = models.IntegerField(default = 0)

    # Used for linking w/ JRC internal system
    jrc_key = models.IntegerField(unique = True, blank = True)

    filename = models.CharField(max_length = 256, blank = True)

    created_at = models.DateField(default = datetime.datetime.now())


    
    def save(self, *args, **kwargs):
        """ Returns max PK of table `jrc_parameter` plus 1 """

        if self.jrc_key == None or self.jrc_key == 0:

            jrc_key = 1
            maxId = -1
            
            cursor = connection.cursor()
    #        cursor.execute("SET SESSION query_cache_type = OFF")
            cursor.execute("SELECT MAX( jrc_key ) FROM `jrcmodel_jrcparameter`")
            maxId = cursor.fetchone()
            print "Max JRC key row:"
            print maxId
    
    #        key_mask = JrcConfig.get_value_by_name("PARAMETER_KEY", "XXX%d")
    
            if (maxId != None) and (maxId[0] != None):
                jrc_key = int(maxId[0]) + 1
    
            self.jrc_key = jrc_key
        else:
            print "JRC key is already set, do not change it"

        super(JrcParameter, self).save(*args, **kwargs)
    
    def get_jrc_key(self):
        key_mask = JrcConfig.get_value_by_name("PARAMETER_KEY", "XXX%d")
        return key_mask % self.jrc_key

    def status_name(self):
        return MyConstants.get_status_name(self.status_id)
    
    def direction_name(self):
        return MyConstants.get_direction_name(self.direction_id)
    
    def asset_class(self):
        return self.market_id.asset
    
    def market_name(self):
        return self.market_id.name
    
    def cash_or_future_name(self):
        if self.cash_or_future == "C":
            return "Cash"
        return "Future"

    def __unicode__(self):
        return "%s %s, %s" % (self.market_name(), self.direction_name(), self.get_jrc_key())


# CST renamed to CST for use with SQLite!
class CST(models.Model):
    status_id = models.IntegerField(default = 1, choices = MyConstants.CST_STATUS_CHOICES)

    market_direction_1 = models.ForeignKey(JrcParameter)
    market_direction_2 = models.ForeignKey(JrcParameter, blank = True, null = True, related_name = "+")

    # Candle interval (minutes)
    timelevel_m = models.IntegerField(default = 60)
#    tmlvl_m = models.ForeignKey(TimeLevel) 
    
    name = models.CharField(max_length = 128, blank = True)
    remarks = models.TextField(blank = True)
    # Just to ease search
    label = models.CharField(max_length = 32, blank = True)

    # For connection with Tradestation
    session = models.CharField(max_length = 64, choices = MyConstants.SESSION_CHOICES)

    fees = models.DecimalField(max_digits = 12, decimal_places = 2)

    created_at = models.DateTimeField(auto_now_add = True)
    
    def market_direction_name_1(self):
        return MyConstants.get_direction_name(self.market_direction_1)

    def market_direction_name_2(self):
        return MyConstants.get_direction_name(self.market_direction_2)

    def status_name(self):
        ar = { MyConstants.CST_STATUS_TESTED: "tested", MyConstants.CST_STATUS_IMPLEMENTED: "implemented", MyConstants.CST_STATUS_SORTED_OUT: "sorted out", MyConstants.CST_STATUS_DELETED: "deleted" }
#        if self.status_id == MyConstants.CST_STATUS_TESTED:
        if ar.has_key(self.status_id):
            return ar[self.status_id]
        return "?%d?" % self.status_id
    
    def session_name(self):
        return MyConstants.get_session_name(self.session)
    
    def slug_name(self):
        if len(self.name) > 0:
            return self.name.replace("&", "").replace(" ", "-").lower()
        return ""
    
    def __unicode__(self):
        return "%s" % self.name


class Portfolio(models.Model):
    csts = models.ManyToManyField(CST)

    name = models.CharField(max_length = 128)
    status_id = models.IntegerField(default = 1, choices = MyConstants.STATUS_CHOICES)
    priority = models.IntegerField(default = 0)
    weighting = models.FloatField()
    
    def cst_qnt(self):
        return "%d" % self.csts.count()
    
    def __unicode__(self):
        return "%s" % self.name
    
    def status_name(self):
        return "%s" % MyConstants.get_status_name(self.status_id)
    
class EuroConv(models.Model):
    status_id = models.IntegerField(default = MyConstants.STATUS_APPROVED, choices = MyConstants.STATUS_CHOICES)
    
    year = models.IntegerField()
    month = models.IntegerField()

    EURUSD = models.DecimalField(max_digits = 10, decimal_places = 5)
    EURJPY = models.DecimalField(max_digits = 10, decimal_places = 5)
    EURGBP = models.DecimalField(max_digits = 10, decimal_places = 5)
    EURCHF = models.DecimalField(max_digits = 10, decimal_places = 5)
    EURAUD = models.DecimalField(max_digits = 10, decimal_places = 5)
    EURNZD = models.DecimalField(max_digits = 10, decimal_places = 5)
    EURCAD = models.DecimalField(max_digits = 10, decimal_places = 5)
    
    def __unicode__(self):
        """ Outputs object as string """
        return "%d-%02d (%s, %s, %s, %s, %s, %s, %s)" % (self.year, self.month, self.EURUSD, self.EURJPY, self.EURGBP, self.EURCHF, self.EURAUD, self.EURNZD, self.EURCAD)

class ArchivePerf(models.Model):
    year = models.IntegerField()
    month = models.IntegerField()
    value = models.DecimalField(max_digits = 18, decimal_places = 14)
    
    # Kind of constants
    _leverage_pct_ratio = 5
    
    def __unicode__(self):
        return "%d-%d: %.14g" % (self.year, self.month, self.value)
    
    def get_performance_leverage_pct(self):
        return self.value * self._leverage_pct_ratio
    
    def calculate_leverage_performance_pct_year(self, year):
        v = Decimal(0)
        for p in ArchivePerf.objects.filter(year = year):
            v = v + p.get_performance_leverage_pct()
        return v
    
    def calculate_unleverage_performance_pct_year(self, year):
        v = Decimal(0)
        for p in ArchivePerf.objects.filter(year = year):
            v = v + p.value
        return v

class DvRatio(models.Model):
    year = models.IntegerField()
    month = models.IntegerField()
    value = models.DecimalField(max_digits = 10, decimal_places = 4)
    
    _archive_performance = None
    
    def __unicode__(self):
        return "%d-%d: %.14g" % (self.year, self.month, self.value)
    
    def get_risk_free_interest_per_month(self):
        return self.value / 12

    def get_related_performance(self):
        """
            Returns ArchivePerf object for this date
        """
        if self._archive_performance == None:
            try:
                self._archive_performance = ArchivePerf.objects.filter(year = self.year).get(month = self.month)
            except (DvRatio.MultipleObjectsReturned, DvRatio.DoesNotExist):
                return None

        return self._archive_performance
    
    def get_additional_performance(self):
        archive_perf = self.get_related_performance()
        if archive_perf == None:
            return 0
        
        return archive_perf.value - self.get_risk_free_interest_per_month()
    
    def get_downside_vola_calculation(self):
        v = self.get_additional_performance()
        if v < 0:
            v *= v
        else:
            v = 0
            
        return v 


class ProfitLossesValues(models.Model):
    status_id = models.IntegerField(default = MyConstants.STATUS_APPROVED, choices = MyConstants.STATUS_CHOICES)
    year = models.IntegerField()
#    asset_class = models.CharField(max_length = 2, choices = MyConstants.ASSET_CLASSES_CHOICES)
    month = models.IntegerField()
    market = models.ForeignKey(Market)
#    exchange_name = models.CharField(max_length = 128)
    value = models.DecimalField(max_digits = 7, decimal_places = 1)

    def get_asset_class(self):
        return self.market.asset

    def __unicode__(self):
        return "%s %d-%d = %s" % (self.market.name, self.year, self.month, self.value)
    

class FuturesTradesAmount(models.Model):
    status_id = models.IntegerField(default = MyConstants.STATUS_APPROVED, choices = MyConstants.STATUS_CHOICES)
    cst = models.ForeignKey(CST)
    year = models.IntegerField()
    month = models.IntegerField()
    amount = models.IntegerField(default = 0)
    
    def __unicode__(self):
        return "%s %d-%d: %d" % (self.cst.name, self.year, self.month, self.amount)

class FuturesContractsAmount(models.Model):
    status_id = models.IntegerField(default = MyConstants.STATUS_APPROVED, choices = MyConstants.STATUS_CHOICES)
    cst = models.ForeignKey(CST)
    year = models.IntegerField()
    month = models.IntegerField()
    amount = models.IntegerField(default = 0)
    
    def __unicode__(self):
        return "%s %d-%d: %d" % (self.cst.name, self.year, self.month, self.amount)
